home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / LISP Related / U. Mass AI & LISP Tools / MODULES / DNET / DNET.lisp < prev   
Encoding:
Text File  |  1990-06-24  |  71.1 KB  |  1,523 lines  |  [TEXT/MACA]

  1. ; (c) Copyright 1990 by University of Massachusetts. All rights reserved.
  2. ; This software was conceived, designed, and written by Dan Suthers 
  3. ; while supported by the National Science Foundation under grant number
  4. ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
  5. ; CA.  Partial support was also received from the Office of Naval Research
  6. ; under a University Research Initiative Grant, contract N00014-86-K-0764.
  7. ; Mr. Suthers created this software under his own initiative while in an 
  8. ; academic relationship with the University of Massachusetts.  The above
  9. ; copyright notice was a condition placed by University lawyers on approval
  10. ; of distribution of this software by Apple Computer, and is not meant to
  11. ; imply that this software was created in an employment or "work for hire"
  12. ; relationship between the University and Mr. Suthers.
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. ;
  15. ; File:         DNET.LISP
  16. ; Author:       Dan Suthers
  17. ; Created:      10-Apr-88 01:48:11
  18. ; Modified:     22-Jun-90 02:31:07 (Dan Suthers)
  19. ; Language:     LISP
  20. ; Package:      DNET
  21. ;
  22. ; Description:  Simple discrimination net for uniquifying list expressions
  23. ;               and associating information with those expressions.
  24. ;               Includes pattern matching and context switching mechanisms.
  25. ;
  26. ; (c) Copyright 1988, by Daniel D. Suthers
  27. ;                        Department of Computer and Information Science
  28. ;                        University of Massachusetts
  29. ;                        Amherst, Massachusetts 01003
  30. ;
  31. ; This software was conceived, designed, and written by Dan Suthers 
  32. ; while supported by the National Science Foundation under grant number
  33. ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
  34. ; CA.  Partial support was also received from the Office of Naval Research
  35. ; under a University Research Initiative Grant, contract N00014-86-K-0764.
  36. ; I wish to acknowledge the generous support of Beverly Woolf, who obtained 
  37. ; the above grants and encouraged me to pursue my own research interests in
  38. ; her lab.  This work would not have been possible without the resources and
  39. ; stimulating environment of the Computer and Information Science department.
  40. ;
  41. ; Permission to use, modify, and distribute this software is granted subject 
  42. ; to the following restrictions and understandings:
  43. ; 1. The file header, including this notice, shall be retained, and may be
  44. ;    extended to include documentation of modifications to the software.
  45. ; 2. This material is for nonprofit educational and research purposes only.
  46. ;    Users are requested, but not required, to inform Mr. Suthers of any 
  47. ;    noteworthy uses of this software.
  48. ; 3. Mr. Suthers and the University of Massachusetts make no warrantee or
  49. ;    representation that the operation of this software will be error free,
  50. ;    and are under no obligation to provide any services.
  51. ; 4. Any user of such software agrees to indemnify and hold harmless Mr.
  52. ;    Suthers and the University of Massachusetts from all claims arising 
  53. ;    out of the use or misuse of this software, or arising out of any 
  54. ;    accident, injury, or damage whatsoever, and from all costs, counsel
  55. ;    fees, and liabilities incurred in or about any such claim, action, or
  56. ;    proceeding brought thereon.
  57. ; 5. All materials and reports developed as a consequence of the use of 
  58. ;    this software shall duly acknowledge such use, in accordance with
  59. ;    the usual standards of acknowledging credit in academic research.
  60. ;
  61. ; Status:       Stable and well tested.
  62. ;
  63. ; Tested:       Hewlett Packard 9000       02-Nov-88 Dan Suthers
  64. ;               Macintosh II Coral/Allegro 20-Apr-89 Dan Suthers
  65. ;               Texas Instruments Explorer 02-Nov-88 Dan Suthers
  66. ;               VAX/VMS                    20-Apr-89 Dan Suthers
  67. ;
  68. ; Changes:      
  69. ;   25-Jun-88 Type checking, type declarations, and optimizing.
  70. ;   30-Jun-88 New-expr-hook now specific to each dnet.
  71. ;   02-Jul-88 BIND conflicts with TI compiler; renamed to BIND-VARS.
  72. ;   13-Jun-88 Update to new version of SM.
  73. ;   15-Jun-88 Added SUBSTITUTE-VARS.
  74. ;   20-Jul-88 SAVE-DNET now saves with the explicit name; no *dnet*.
  75. ;     Dotted lists checked for by INDEXPR, since we optimize out any
  76. ;     type checking once past the argument checking.  Dnet-Terminals
  77. ;     no longer in SM.
  78. ;   30-Jul-88 DNET-ROOT -> DNET (this was an artifact); DNET-TERMINALS
  79. ;    eliminated to save space; INDEXPR-HOOK and DELEXPR-HOOK instead of
  80. ;    NEW-EXPR-HOOK; changes to MAKE-DNET syntax; VARIABLE -> DEFVARIABLE;
  81. ;    proclamations.
  82. ;  01-Nov-88 Update to reflect SM changes.
  83. ;  16-Nov-88 Added MATCH and UNIFY; made optimize declarations better;
  84. ;    made internal versions of functions that don't check args.
  85. ;  20-Nov-88 MATCH-PATTERN handles &rest "dotted variables": (a b . ?:x);
  86. ;    match-x-internal takes previous-bindings argument (for client's sake).
  87. ;  24-Nov-88 Added MAP-DNET-TERMINALS.
  88. ;  10-Dec-88 Added RESET-DNET; SAVE-DNET now finds variables in INFO too.
  89. ;  17-Dec-88 ? package now does not use any other package, so symbols in
  90. ;    package LISP won't screw it up when declared as variables in ?.
  91. ;    DEFVARIABLE now takes strings as well as symbols.  
  92. ;  23-Dec-88 SUBSTITUTE-VARS renamed SUBSTITUTE-BINDINGS.  Added version
  93. ;    called SUBSTITUTE-TRANSITIVE-BINDINGS for ((?:x . 3) (?:y . ?:x)). 
  94. ;  16-Jan-89 SAVE-DNET uses INDEXPR-INTERNAL for faster load.
  95. ;  22-Mar-89 Added PATTERN-P; Made UNIFY and MATCH return 3 values to 
  96. ;    separate the bindings in each direction.
  97. ;  24-Nov-89 Updating documentation only.
  98. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  99. ;                          USER'S DOCUMENTATION
  100. ;
  101. ;  A discrimination net facility lets one do several things:
  102. ;
  103. ;  - 'Uniquify' list expressions under EQ by allowing one to retrive a stored
  104. ;    expression using an EQUAL expression as the key.
  105. ;  - Associate properties with list expressions (using the above capability).
  106. ;  - Ask whether a particular expression has been stored in a data base.
  107. ;  - Retrieve expressions by pattern matching using variables in either the
  108. ;    retrieval key or in the stored expressions.
  109. ;  - Change contexts efficiently by changing the discrimination net in use.
  110. ;
  111. ;  A discrimination net may be used to implement a simple data base, or to
  112. ;  support more sophisticated systems for deductive retrieval, forward and
  113. ;  backward chaining of rules, and/or truth maintenance.  The present package
  114. ;  attempts to be simple, general, and efficient by providing the most basic
  115. ;  operations efficiently implemented without frills.
  116. ;
  117. ;  Most operations take the discrimination net as their last argument.  This
  118. ;  facilitates context switching.  Storage of dotted lists is not allowed, 
  119. ;  due to the indexing method used.  Pattern matching retrieval processes 
  120. ;  variables in either the query pattern (MATCH-PATTERN) or the network 
  121. ;  (MATCH-EXPRESSION), or both (MATCH). The general MATCH function is less 
  122. ;  efficient, and most applications only need to match in one direction.
  123. ;
  124. ;  When a new expression is added via INDEXPR, some applications will need 
  125. ;  to do special processing of the expression and/or its dnet-terminal.  If 
  126. ;  this were left to the application after the INDEXPR call returned, this 
  127. ;  processing could not be done on expressions loaded from a file saved by 
  128. ;  SAVE-DNET, since the latter writes calls to INDEXPR with no surrounding 
  129. ;  application-specific forms.  The solution is to have an INDEXPR-HOOK, 
  130. ;  which when non-nil is a lambda called on all newly indexed expressions 
  131. ;  and their dnet-terminals.  There is also a corresponding DELEXPR-HOOK.
  132. ;
  133. ; Discrimination Net Operations:  
  134. ;    MAKE-DNET makes new ones.
  135. ;    RESET-DNET resets to empty and modifies associated info.
  136. ;    DNET-INFO associates information with a dnet.
  137. ;    ALL-EXPRESSIONS returns all expressions in a dnet (slow).
  138. ;    MAP-DNET-TERMINALS maps a function across all dnet-terminals in a dnet.
  139. ;    DESTROY-DNET undefines and deallocates a dnet.
  140. ;    SAVE-DNET saves a dnet to a file (slow).
  141. ;  (Functions requiring retrieval of all expressions are slow because DNET is
  142. ;  optimized for balancing fast access to single expressions with space economy.)
  143. ; Expression-Based Operations:
  144. ;  These do not process variables (treat variables like any other atom).
  145. ;   INDEXPR puts an expression in a dnet.
  146. ;   GETEXPR retrieves an expression from a dnet.
  147. ;   DELEXPR deletes an expression from a dnet.
  148. ;   EXPR-INFO associates information with an expression in a dnet.
  149. ;
  150. ; Pattern-Based Operations:
  151. ;  A pattern is a symbolic expression which may contain variables.  
  152. ;  Variables are symbols in the package ?.  It is recommended that
  153. ;  one declare all variables before use with the (defvariable <sym>) form.
  154. ;  No symbol should ever be uninterned from ? by another program.
  155. ;   DEFVARIABLE defines a variable.
  156. ;   VARIABLE-P tests whether an object is a variable. 
  157. ;   PATTERN-P tests whether an object is an expression containing a variable.
  158. ;   VARIABLES-IN-PATTERN returns a list of variables in a pattern.
  159. ;   MATCH retrieves patterns (or expressions) matching a given pattern (expression).
  160. ;   MATCH-PATTERN retrieves expressions matching a given pattern.
  161. ;   MATCH-EXPRESSION retrieves patterns matching a given expression.
  162. ;   BIND-VARS does the variable binding (restricted unification) for one-way
  163. ;     match candidates, and is exported for its potential usefulness.
  164. ;   UNIFY does bidirectional variable binding (for MATCH), and is exported.
  165. ;   SUBSTITUTE-BINDINGS substitutes for variables in a pattern to give an
  166. ;     expression, given a binding list.
  167. ;
  168. ; NOTE ON BEHAVIOR -- The following behavior is CORRECT:
  169. ;
  170. ;   ? (dnet:make-dnet :test-dnet)
  171. ;   ? (dnet:indexpr '(a b c) :test-dnet)
  172. ;   ? (dnet:indexpr '(a ?:x c) :test-dnet)
  173. ;   ? (dnet:indexpr '(a ?:y c) :test-dnet)
  174. ;   ? (dnet:indexpr '(a (?:x y z) c) :test-dnet)
  175. ;
  176. ;   ? (dnet:match-pattern '(a ?:x c) :test-dnet)
  177. ;   ((A (?:X Y Z) C) (A ?:Y C)     (A ?:X C)     (A B C))
  178. ;   (((?:X ?:X Y Z)) ((?:X . ?:Y)) ((?:X . ?:X)) ((?:X . B)))
  179. ;
  180. ;   ? (dnet:match-expression '(a ?:x c) :test-dnet)
  181. ;   ((A ?:X C)     (A ?:Y C))
  182. ;   (((?:X . ?:X)) ((?:Y . ?:X)))
  183. ;
  184. ;   ? (dnet:match '(a ?:x c) :test-dnet)
  185. ;   ((A ?:Y C)    (A ?:X C) (A B C))
  186. ;   (((?:X . ?:Y)) NIL      ((?:X . B)))
  187. ;   (NIL           NIL      NIL)
  188. ;
  189. ; MATCH-PATTERN ignores variables in the DNET.  Thus, (?:X Y Z) is a
  190. ;   constant list as far as it is concerned, and there is no contradiction
  191. ;   to binding ?:X to (?:X Y Z).  This feature may be useful when trying
  192. ;   to retrieve patterns without processing their variables.
  193. ; MATCH-EXPRESSION treats the ?:x in the query as a constant, so only
  194. ;   returns the patterns in the DNET which match it.  One happens to have
  195. ;   a variable which is the same as the constant; the other matches the
  196. ;   variable ?:y in DNET to the constant ?:x.
  197. ; MATCH will only return patterns which logically unify with the query
  198. ;   pattern.  Thus, it is correctly more restrictive than MATCH-PATTERN
  199. ;   above, as ?:X cannot be bound to an expression containing itself.
  200. ;   While MATCH returns bindings in both directions (the second and third
  201. ;   values), (?:Y . ?:X) does not appear in the second binding list for
  202. ;   (A ?:Y C) because (?:X . ?:Y) already expressed the binding, "using 
  203. ;   up" ?:Y.  I.e., there is no redundancy across the binding lists.
  204. ;
  205. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  206. ;
  207. ;                          Implementation Notes
  208. ; The implementation relies on the SM package, which contains a Structure
  209. ; manager for Common Lisp structures.  Discrimination nets are implemented
  210. ; as SM objects of type DNET, and the data structures in which expressions
  211. ; are stored use CL structures of type DNET-TERMINAL.  While DNET may be used
  212. ; by client programs ignorant of SM, it provides access to these SM objects 
  213. ; in a manner which a SM-knowledgable client may use to increase efficiency.
  214. ;
  215. ; This version uses nested association lists to represent the discrimination
  216. ; net.  This network will be quite big if printed, so this should be avoided.
  217. ; Use of an SM object name to identify DNETs prevents printing the network 
  218. ; inadvertently when tracing function calls.
  219. ;
  220. ; Each DNET has a LINK slot containing the discrimination net.  An INFO slot
  221. ; allows the client program to record related information.
  222. ;
  223. ; Terminals in the network are CL structures of type DNET-TERMINAL.  The EXPR
  224. ; slot gives the expression indexed, and the INFO slot is used by the client
  225. ; to associate properties with the expression. These are represented as vector
  226. ; structures for efficiency, and they are un-named for economy of space.
  227. ;
  228. ; Why don't I just store the INFO at the terminal, and not bother saving the
  229. ; EXPR, which presumably we already know anyway by virtue of having found a 
  230. ; path to the terminal?  The pattern matchers, which may reach a multitude of 
  231. ; terminals via variable matching, have to return all the expressions which 
  232. ; got them there.  The matching code is much easier to write if it doesn't 
  233. ; have to keep track of how it got to the terminal in order to cons up and 
  234. ; return the expression corresponding to the path traversed.  By storing the 
  235. ; expression, we avoid having to cons up a new version of it every time its 
  236. ; terminal is reached.  So at a small space penalty the code is simpler, 
  237. ; faster, and costs less garbage collection.
  238. ;
  239. ; ----------------------------------
  240. ; Traversing the Discrimination Net:
  241. ;
  242. ; At any given time we need a handle on a place in the dnet which allows us
  243. ; to add new branches.  The fundamental unit is called a LINK, and consists
  244. ; of a cons of the key that got us to where we are and the association list
  245. ; which takes us out of where we are: 
  246. ;               (key . ((k1 . ...) ... (kn . ....))).
  247. ; In most of the code, if a function is called with the above link, it may
  248. ; assume that we have already "consumed" the <key>, and the association list
  249. ; in the cdr represents the branches in the dnet which may be taken.
  250. ; Traversal of a link consists of selecting one of the (ki . ...) items to
  251. ; become the new link.  Since each function sees the entire link, it can 
  252. ; push new items onto or replace its alist.
  253. ;
  254. ; -----------------
  255. ; Pattern Matching:
  256. ;
  257. ; Pattern matching works by first retrieving candidate matches by processing
  258. ; variables without checking consistency of bindings, then checking this
  259. ; consistency with a unifier or partial unifier.  (Only MATCH uses the more
  260. ; expensive full unifier.)  The reasons for taking this simple approach, 
  261. ; rather than doing retrieval and unification all at once, are:
  262. ; 1. Code Complexity: Recording bindings dictates continuation in recursive 
  263. ;    call with extended binding list.  But continuing at a continuation link
  264. ;    after consuming an item requires that we know what followed a variable;
  265. ;    i.e. works best after we have returned from the recursive call that
  266. ;    consumed items.  A possible solution is to continue in recursive calls,
  267. ;    but add a parameter passed the list the variable was found in, but ...
  268. ; 2. Space: To record bindings so they may be checked and returned, you must
  269. ;    cons up their bindings to the relevant subexpressions, and save these
  270. ;    in an alist. Many of these consed bindings will fail.  In contrast, the
  271. ;    approach implemented here conses up only bindings very likely to succeed.
  272. ; 3: Speed: Solutions to 1 will probably require more parameter passing, or
  273. ;    some other speed tradeoff which reduces gain over re-processing the
  274. ;    expressions in BIND-VARS.  Garbage Collection is increased by 2.
  275. ;
  276. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  277. ;
  278. ;                           Possible Improvements
  279. ;
  280. ; Specialized Unification for Speed:
  281. ;
  282. ; * Determine what has already been verified by the MATCH- internal functions,
  283. ;   and modify UNIFY to take advantage of this.  We loose export of general 
  284. ;   purpose UNIFY (without loss of logical generality), but gain efficiency.
  285. ;
  286. ; COMMENT: I did this for the match-X functions, but not for MATCH.
  287. ;
  288. ; Variable Uniqueness:
  289. ;
  290. ; Come to a decision on the variable uniqueness issue -- are variables
  291. ; assumed to be unique between expressions by not writing code which deals
  292. ; with the case when they are not, *forced* to be by writing code to treat
  293. ; the same ? symbol in different expressions as different, or do I write
  294. ; a more general unifier?  Not sure if this will help anything.
  295. ;
  296. ; DNET Optimization:
  297. ;
  298. ; The next two ideas have to do with ways to optimize the space/time 
  299. ; performance of DNETs.  I don't plan on on-line transformations because 
  300. ; it would be too expensive to decide if they are needed every time we
  301. ; do an insertion or deletion.  Instead, a single function, called 
  302. ; OPTIMIZE-DNET, could perform the desired transformations based on the
  303. ; current contents of the DNET, and could be called by the user after
  304. ; loading a static DNET, or periodically or after major changes.
  305. ; * Hash Tables for Speed:
  306. ;   - Modify the DNET functions to use either hash tables or alists depending
  307. ;     on which is found in the CDR of a link. 
  308. ;   - Then write a routine that recasts all a-lists above a certain length as 
  309. ;     hash tables.  (Hash tables are empirically faster for even tables/lists 
  310. ;     of 3 in CCL.  However, CCL and the HP both have a minimum table size of 
  311. ;     37, which should be considered when deciding at what threshold to 
  312. ;     convert.
  313. ;
  314. ; COMMENT: I tried this.  Unfortunately the minimum hash table size makes 
  315. ; this a space-hog.  Worse, speed tests showed the mixed representation
  316. ; was SLOWER, presumably because one must test at each link for whether 
  317. ; a hash table or alist is used.  I wanted to test a pure hash table 
  318. ; representation, but ran out of memory under this rep (in 4 meg). 
  319. ;
  320. ; * Compressing Linear Paths for Space Reduction:
  321. ;   - Come up with a representation for linear paths, eg. a distinguishable
  322. ;     CDR object in the links.
  323. ;   - Modify search and deletion functions to use it.
  324. ;   - Write a transformation routine for compression. 
  325. ;   - Note that here we must violate strict off-line transformations: an
  326. ;     insertion may force decompression of a (formerly) linear path.
  327. ;
  328. ; Child DNETs for Context Hierarchies:
  329. ;
  330. ; Implement a facility where a DNET may have children who inherit the
  331. ; expressions in the parent DNET during retrieval.  Insertion occurs
  332. ; in the specified DNET only.  (Perhaps assume the child does not exist
  333. ; independent of the parent, so insertion into child occurs only if the
  334. ; expression is not already in the parent.  But expressions not in the
  335. ; parent can be inserted into either parent or child.)
  336. ;
  337. ; TMS:
  338. ;
  339. ; Write additional code in a separate file that implements a TMS, using
  340. ; the EXPR-INFO to store justifications and INDEXPR/DELEXPR-HOOKs to 
  341. ; do the truth maintenance.
  342. ;
  343. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  344.  
  345. (in-package :DNET)
  346.  
  347. (export '(
  348.  
  349.           *?-package*
  350.           *dnet-package*
  351.  
  352.           ;; For dnets
  353.  
  354.           all-expressions
  355.           destroy-dnet
  356.           dnet-info
  357.           make-dnet
  358.           map-dnet-terminals
  359.           reset-dnet
  360.           save-dnet
  361.  
  362.           ;; For expressions
  363.  
  364.           delexpr
  365.           getexpr
  366.           indexpr
  367.           expr-info
  368.           not-a-dotted-list
  369.  
  370.           ;; Pattern matching
  371.  
  372.           bind-vars
  373.           defvariable
  374.           match
  375.           match-expression
  376.           match-pattern
  377.           pattern-p
  378.           substitute-bindings
  379.           substitute-transitive-bindings
  380.           unify
  381.           variable-p
  382.           variables-in-pattern
  383.  
  384.           ;; For clients wishing to access internal SM representations:
  385.  
  386.           dnet
  387.           dnet-info-place
  388.           dnet-delexpr-hook
  389.           dnet-indexpr-hook
  390.           dnet-compiled-delexpr-hook
  391.           dnet-compiled-indexpr-hook
  392.           dnet-terminal-expr
  393.           dnet-terminal-info
  394.  
  395.           ))
  396.  
  397. (require :MAPPINGS)
  398. (require :SM)
  399.  
  400. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  401. ;;; OPTIMIZING IN ...
  402. ;;; ----------------
  403. ;;; CORAL'S ALLEGRO:
  404. ;;; - The safety 1 space 2 speed 2 setting lets the compiler trust all
  405. ;;; type declarations, and eliminates event-processing in iterative loops.
  406. ;;; - We crank this up to safety 1 space 2 speed 3 for heavy computation,
  407. ;;; so fixnum operations are guaranteed to return fixnums, and car and
  408. ;;; cdr don't check types (but an error would crash Allegro).
  409. ;;; - A drop to safety 0 would eliminate number of argument and stack 
  410. ;;; overflow checks, skip some event processing, and make slot access 
  411. ;;; open coded with no type checking. Risky.
  412. ;;; -----------------
  413. ;;; HP's Common Lisp:
  414. ;;; Only safety and speed are used.  This only affects compiled code.
  415. ;;; - Safety 0 supresses argument count check.
  416. ;;; - Speed 2 does constant folding, "safe tranforms" on function calling,
  417. ;;; conversion of &rest and &keyword to positional, and open coding where declared.
  418. ;;; - Speed 3 supresses argument count check (as if safety 0); makes structure slot
  419. ;;; access and setf inline with no checking; and additional functional transforms.
  420.  
  421. (proclaim '(optimize (safety 1) (space 2) (speed 2)))
  422.  
  423. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  424. ;;;
  425. ;;;                         DATA STRUCTURES
  426. ;;;
  427. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  428.  
  429. (deftype boolean () '(or T null))
  430.  
  431. (defstruct (DNET-TERMINAL (:type vector) ; don't waste space with label
  432.                           (:constructor make-dnet-terminal (expr info)))
  433.   (EXPR nil :type T :read-only t)
  434.   (INFO nil :type T :read-only nil))
  435.  
  436. (defparameter *DNET-PACKAGE* (find-package "DNET"))
  437.  
  438. ;;; CCL Belched without this eval-when.
  439.  
  440. (eval-when (eval load compile)
  441.   (defparameter *?-PACKAGE* (or (find-package "?") (make-package "?")))
  442.   ;;; Need to un-use all packages (eg LISP) so a symbol declared as a variable 
  443.   ;; in ? will indeed be a symbol in ?, and hence treated as a variable.
  444.   (unuse-package (package-use-list *?-package*) *?-package*))
  445.  
  446. (sm:dst (DNET
  447.          (:reusable nil)
  448.          (:sort-instances t)
  449.          (:comments "
  450.   Discrimination NETwork root node, which provides a handle on an entire DNET.
  451.   Discrimination networks are used to manage a database of arbitrary list and
  452.   symbol expressions, and do retrieval using pattern matching on expressions 
  453.   with variables in them: see documentation in the source file.  Instances are
  454.   not reusable since there are many conses in the LINK slot to be reclaimed.  
  455.   DNETs saved to a file by SM:PRINTS will be empty when loaded: use SAVE-DNET."))
  456.  
  457.         (LINK      (list :head)
  458.                    :type list 
  459.                    :computed t
  460.                    :comments "
  461.     This slot contains the entire discrimination network, in the form of a nested
  462.     association list (can be large).  Called a link since, like all its recursive
  463.     components, it consists of a cons of a key (in this case, the name of the DNET)
  464.     and a list of other links (consequently, an alist).")
  465.  
  466.         (INDEXPR-HOOK nil
  467.                       :type list
  468.                       :comments "
  469.     If non-NIL, contains a lambda form.  The first time an expression is indexed
  470.     into the DNET, if this is non-nil its compiled version (see next slot) is 
  471.     called on two arguments: the expression, and its dnet-terminal structure.")
  472.  
  473.         (COMPILED-INDEXPR-HOOK 
  474.          nil
  475.          :type (or null function)
  476.          :computed T
  477.          :comments "
  478.     If non-NIL, contains the compiled functional version of the lambda form 
  479.     stored in INDEXPR-HOOK.")
  480.  
  481.         (DELEXPR-HOOK nil
  482.                       :type list
  483.                       :comments "
  484.     If non-NIL, contains a lambda form.  When an expression is successfully
  485.     deleted from the DNET, if this is non-nil its compiled version (see next slot)
  486.     is called on two arguments: the expression, and its dnet-terminal structure.")
  487.  
  488.         (COMPILED-DELEXPR-HOOK 
  489.          nil
  490.          :type (or null function)
  491.          :computed T
  492.          :comments "
  493.     If non-NIL, contains the compiled functional version of the lambda form 
  494.     stored in DELEXPR-HOOK.")
  495.  
  496.         (INFO-PLACE nil 
  497.                     :type T
  498.                     :comments "
  499.     The user may associate arbitrary information with the DNET by storing it 
  500.     in this slot, SETF accessable using DNET-INFO."))
  501.  
  502. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  503. ;;;
  504. ;;;                     INTERNAL FUNCTIONS AND MACROS
  505. ;;;
  506. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  507. ;;; Many of these are not needed by the user. To save space, the macros 
  508. ;;; are not loaded in compiled files.
  509.  
  510. (defun NOT-A-DOTTED-LIST (expr)
  511.   "not-a-dotted-list <expr>                                        [Function]
  512.   Returns T iff there is no non-nil atomic CDR in <expr>."
  513.   (declare (optimize (safety 1) (space 2) (speed 3)))
  514.   (cond ((null expr) T)
  515.         ((atom expr) T)
  516.         ((and (cdr expr) (atom (cdr expr))) nil)
  517.         (t (and (not-a-dotted-list (car expr))
  518.                 (not-a-dotted-list (cdr expr))))))
  519.  
  520. ;;; Basic link operations.
  521. (eval-when (compile eval)
  522.   
  523.   (defmacro TRAVERSE-LINK (key link)
  524.     ;; The cdr is the association list.
  525.     `(assoc ,key (cdr ,link) :test #'equal)) ; (the list (cdr ,link)) but CCL has bug
  526.   
  527.   (defmacro ADD-LINK (key link)
  528.     ;; Must return the link just created (not the link it is added to),
  529.     ;; so construction may continue from the returned result.
  530.     `(or (assoc ,key (cdr ,link) :test #'equal)  ; (the list (cdr ,link)) but CCL has bug
  531.          (let ((newlink (cons ,key nil)))
  532.            (declare (cons newlink))
  533.            (push newlink (cdr ,link))  ; (the list (cdr ,link)) but CCL has bug
  534.            newlink)))
  535.   )
  536.  
  537. ;;;------------------------------------------
  538. ;;; Finding the node of something in the net.
  539.  
  540. ;;; This is not eval-when eval compile because EXPR-INFO-INTERNAL, used by other
  541. ;;; DNET files, expands into a form which contain this.  So it needs to be loaded.
  542.  
  543. (defmacro FIND-TERMINAL-LINK (expr link)
  544.   `(cond ((null ,link) nil)
  545.          ((atom ,expr) (traverse-link ,expr ,link))   ; nil caught here ...
  546.          ((list-find-terminal-link ,expr ,link))))    ; so never passed here ...
  547.  
  548. (defun LIST-FIND-TERMINAL-LINK (list link)
  549.   (declare (list list link) (optimize (safety 1) (space 2) (speed 3)))
  550.   (do ((lptr list (cdr lptr))
  551.        (curlink (traverse-link :begin-list link)))
  552.       ((null lptr) (traverse-link :end-list curlink)) ; relies on list never nil
  553.     (declare (list lptr curlink))
  554.     (if (null curlink) (return nil))
  555.     (setf curlink (find-terminal-link (car lptr) curlink))))
  556. (proclaim '(function list-find-terminal-link (list list) list))
  557.  
  558. ;;;----------------------------------------------
  559. ;;; Inserting links to a new terminal in the net.
  560.  
  561. (eval-when (compile eval)
  562.   (defmacro LINK-TO-TERMINAL (expr link)
  563.     `(if (atom ,expr)
  564.        (add-link ,expr ,link)
  565.        (list-link-to-terminal ,expr ,link))))
  566.   
  567. (defun LIST-LINK-TO-TERMINAL (list link)
  568.   ;; Iterates down list adding items.
  569.   (declare (list list link) (optimize (safety 1) (space 2) (speed 3)))
  570.   (do ((lptr list (cdr lptr))
  571.        (curlink (add-link :begin-list link)))
  572.       ((null lptr) (add-link :end-list curlink))
  573.     (declare (list lptr curlink))
  574.     (setf curlink (link-to-terminal (car lptr) curlink))))
  575. (proclaim '(function  list-link-to-terminal (list list) list))
  576.  
  577. ;;;-----------------------------------------
  578. ;;; Deleting links to a terminal in the net.
  579.   
  580. ;;; Returns DNET-TERMINAL structure (not link) if it was there.  Removes links
  581. ;;; which are no longer needed.  That is, removes that sub-branch of the tree
  582. ;;; which ends in the terminal and is linear (has no branches other than the
  583. ;;; links to the terminal).
  584.  
  585. (eval-when (compile eval)
  586.   (defmacro UNLINK-RETURNING-TERMINAL (expr link)
  587.     ;; Use of *terminal* and catch/throw simplifies the recursive function.
  588.     `(let ((*terminal* nil))
  589.        (declare (special *terminal*) (atom *terminal*))
  590.        (catch :not-found 
  591.          (unlink-using-token-list (list-of-tokens ,expr) ,link)
  592.          *terminal*))))
  593.   
  594. ;; This was difficult to write until I resorted to using a list of tokens.
  595. ;; Now the pattern is simple.  The tokens direct tree traversal. Two base 
  596. ;; conditions: failure (throw to bypass return code) or success (record the
  597. ;; terminal and initiate pruning). Else, recurse on the next link, then 
  598. ;; determine if that link is to be pruned by seeing if its subtree was. A
  599. ;; linear branch has the property that if you delete a link, the enclosing
  600. ;; alist goes to nil, since it only had one link in it (was linear).  Thus
  601. ;; a test of whether the alist of the link is nil determines whether to 
  602. ;; prune it, assuming the recursive pruning was done correctly in the link.
  603.   
  604. (defun UNLINK-USING-TOKEN-LIST (tokens link)
  605.   (declare (special *terminal*) (list tokens link) 
  606.            (optimize (safety 1) (space 2) (speed 3)))
  607.   (cond
  608.    ;; Failure: bypass return code.
  609.    ((null link) (throw :not-found nil))
  610.    
  611.    ;; We have consumed the original expression. If success, save the terminal,
  612.    ;; and initiate pruning by setting the alist of the link to nil.  (This meets
  613.    ;; the assumption of the tests for linearity to be made on the way back up.)
  614.    ((null tokens)
  615.     (if (not (atom (cdr link))) (throw :not-found nil)) ; atom = dnet-terminal
  616.     (setf *terminal* (cdr link))
  617.     (setf (cdr link) nil))
  618.    
  619.    ;; Still more traversing to do.  Traverse the next token/link; recurse
  620.    ;; from there; and prune the branch if it is linear on return.  Test the
  621.    ;; latter condition by seeing if it has a null alist.
  622.    ((let ((next-link (traverse-link (car tokens) link))) ; next place
  623.       (declare (list next-link))
  624.       (unlink-using-token-list (cdr tokens) next-link)   ; recursive work
  625.       (if (null (cdr next-link))                         ; now prune if linear
  626.         (setf (cdr link) (delete next-link (cdr link))))))))
  627. (proclaim '(function unlink-using-token-list (list list) list))
  628.  
  629. (defun LIST-OF-TOKENS (expr)
  630.   ;; Constructs a list of atoms or :begin-list and :end-list keywords
  631.   ;; which uniquely encodes the expression.  These are the keys used
  632.   ;; to index in a discrimination net down to the corresponding terminal.
  633.   (declare (optimize (safety 1) (space 2) (speed 3)))
  634.   (cond 
  635.    ((atom expr) (list expr))
  636.    (t
  637.     (nconc (list :begin-list)
  638.            (reduce #'nconc (mapcar #'list-of-tokens expr))
  639.            (list :end-list)))))
  640. (proclaim '(function list-of-tokens (T) list))
  641.  
  642. ;;;--------------------
  643. ;;; Variable Primitives
  644.  
  645. (defmacro DEFVARIABLE (sym)
  646.   "defvariable <sym>                                                   [Macro]
  647.   Interns the name of <sym> in the variable package ?, and exports it.  Use
  648.   to ensure the variable exists before using it in a pattern.  For example,
  649.   (defvariable x) lets us use ?:x as a variable in patterns."
  650.   `(export (intern 
  651.             ,(if (stringp sym) sym (symbol-name sym))
  652.             *?-package*) *?-package*))
  653.  
  654. (defmacro VARIABLE-P (thing)
  655.   "variable-p <thing>                                                  [Macro]
  656.   Returns T iff <thing> is a variable (symbol in the ? package)."
  657.   `(and (symbolp ,thing)
  658.         (eq (symbol-package ,thing) *?-package*)))
  659.   
  660. (defun VARIABLES-IN-PATTERN (pattern)
  661.   "variables-in-pattern <pattern>                                   [Function]
  662.   Returns a list of variables occuring in <pattern>."
  663.   (declare (optimize (safety 1) (space 2) (speed 3)))
  664.   (cond ((null pattern) nil)
  665.         ((atom pattern) (if (variable-p pattern) (list pattern)))
  666.         (T
  667.          (nunion (variables-in-pattern (car pattern))
  668.                  (variables-in-pattern (cdr pattern))))))
  669. (proclaim '(function variables-in-pattern (T) list))
  670.  
  671. (defun PATTERN-P (expr)
  672.   "pattern-p <expr>                                                 [Function]
  673.   Returns non-NIL value iff <expr> is or contains a variable."
  674.   (if (atom expr)
  675.     (variable-p expr)
  676.     (or (pattern-p (first expr)) (pattern-p (rest expr)))))
  677. (proclaim '(function pattern-p (T) T))
  678.  
  679. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  680. ;;; Internal Expression Functions
  681.  
  682. (defun INDEXPR-INTERNAL (expr dnet info)
  683.   (declare (symbol dnet) (optimize (safety 1) (space 2) (speed 3)))
  684.   (let* ((dnet-struct (sm:gets 'dnet dnet))
  685.          (terminal-link (link-to-terminal expr (dnet-link dnet-struct)))
  686.          (dnet-terminal (cdr terminal-link))
  687.          (added (not dnet-terminal))
  688.          (indexpr-hook (dnet-compiled-indexpr-hook dnet-struct)))
  689.     (declare (type dnet dnet-struct) (list terminal-link) (symbol added)
  690.              (type (or null dnet-terminal) dnet-terminal)
  691.              (type (or null function) indexpr-hook))
  692.     (when added
  693.       (setf dnet-terminal (make-dnet-terminal expr info))
  694.       (setf (cdr terminal-link) dnet-terminal)
  695.       (if indexpr-hook (funcall indexpr-hook expr dnet-terminal)))
  696.     (values added dnet-terminal)))
  697. (proclaim '(function indexpr-internal (t symbol t) (values boolean dnet-terminal)))
  698.  
  699. (defun GETEXPR-INTERNAL (expr dnet)
  700.   (declare (symbol dnet) (optimize (safety 1) (space 2) (speed 3)))
  701.   (let* ((dnet-struct (sm:gets 'dnet dnet))
  702.          (dnet-terminal (cdr (find-terminal-link expr (dnet-link dnet-struct)))))
  703.     (declare (type dnet dnet-struct)
  704.              (type (or null dnet-terminal) dnet-terminal))
  705.     (values
  706.      (if dnet-terminal (dnet-terminal-expr dnet-terminal))
  707.      dnet-terminal)))
  708. (proclaim '(function getexpr-internal (t symbol) (values t dnet-terminal)))
  709.  
  710. (defun DELEXPR-INTERNAL (expr dnet)
  711.   (declare (symbol dnet) (optimize (safety 1) (space 2) (speed 3)))
  712.   (let* ((dnet-struct (sm:gets 'dnet dnet))
  713.          (dnet-terminal (unlink-returning-terminal expr (dnet-link dnet-struct)))
  714.          (delexpr-hook  (dnet-compiled-delexpr-hook dnet-struct)))
  715.     (declare (type dnet dnet-struct)
  716.              (type (or null dnet-terminal) dnet-terminal)
  717.              (type (or null function) delexpr-hook))
  718.     (when dnet-terminal
  719.       (if delexpr-hook (funcall delexpr-hook expr dnet-terminal))
  720.       dnet-terminal)))
  721. (proclaim '(function delexpr-internal (t symbol) (or null dnet-terminal)))
  722.  
  723. ;;; NOTE this is used by RULE package.
  724.  
  725. (defmacro EXPR-INFO-INTERNAL (expr dnet)
  726.   `(dnet-terminal-info
  727.     (cdr (find-terminal-link ,expr
  728.                              (dnet-link (the dnet 
  729.                                              (sm:gets 'dnet ,dnet)))))))
  730.  
  731. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  732. ;;;
  733. ;;;                      INTERNAL PATTERN MATCHING CODE
  734. ;;;
  735. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  736.  
  737. (eval-when (compile eval)
  738.   (defmacro CONTINUATIONS-AFTER-PATTERN-VARIABLE (link)
  739.     ;; Returns a list of links reached by consuming one item from <link>.
  740.     ;; The car of <link> has already been consumed, so we are concerned
  741.     ;; with the branches represented by its alist.
  742.     `(let ((alist (cdr ,link)))
  743.        (unless (atom alist)
  744.          (do ((alptr alist (cdr alptr))
  745.               (continuations nil))
  746.              ((null alptr) continuations)
  747.            (declare (list alptr continuations))
  748.            (if (eq (caar alptr) :begin-list)
  749.              (setf continuations
  750.                    (nconc  continuations
  751.                            (the list (continuations-after-consuming-list (car alptr)))))
  752.              (push (car alptr) continuations)))))))
  753.  
  754. (defun CONTINUATIONS-AFTER-CONSUMING-LIST (link)
  755.   ;; Called when <link> has :begin-list as its car, or when processing dotted
  756.   ;; variables, its job is to return a list of links whose cars are the matching 
  757.   ;; :end-lists.
  758.   (declare (list link) (optimize (safety 1) (space 2) (speed 3)))
  759.   (do ((frontier (cdr link))
  760.        (continuations nil))
  761.       ((null frontier) continuations)
  762.     (declare (list frontier continuations))
  763.     ;; A frontier is a point where we are searching a branch for a corresponding
  764.     ;; :end-list.  As long as no new :begin-lists are encountered, the frontier
  765.     ;; is extended just by cdr-ing until :end-list found.  Otherwise recurse.
  766.     (do  ((fptr frontier (cdr fptr))
  767.           (new-frontier nil))
  768.          ((null fptr) (setf frontier new-frontier))
  769.       (declare (list fptr new-frontier))
  770.       (cond
  771.        ;; Found matching endlist: record continuation.
  772.        ((eq (caar fptr) :end-list) (push (car fptr) continuations))
  773.        ;; New list: get recursive continuations, take one step on each to
  774.        ;; knock off recursive :end-lists, yielding new frontier links.
  775.        ((eq (caar fptr) :begin-list)
  776.         (dolist (rcont-link (the list (continuations-after-consuming-list (car fptr))))
  777.           (declare (list rcont-link))
  778.           (setf new-frontier 
  779.                 (append new-frontier (cdr rcont-link))))) ; 1st copied, not 2nd.
  780.        ;; Otherwise move one step down list.
  781.        (T (setf new-frontier
  782.                 (append new-frontier (cdar fptr))))))))
  783. (proclaim '(function continuations-after-consuming-list (list) list))
  784.  
  785. (eval-when (compile eval)
  786.   (defmacro TRAVERSE-LINKS (key frontier)
  787.     ;; Given a search frontier of links, expands that frontier to the links
  788.     ;; reached via the key, pruning any links that can't be traversed.
  789.     `(mapcan 
  790.       #'(lambda (link &aux (new-link nil))
  791.           (declare (list link new-link))
  792.           (if (and (listp (cdr link))
  793.                    (setf new-link (traverse-link ,key link)))
  794.             (list new-link)
  795.             nil))
  796.       ,frontier)))
  797.  
  798. (defun PATTERN-MATCH-LINKS (remaining-pattern frontier)
  799.   ;; Frontier is a list of links called "continuations": these are the 
  800.   ;; locations in the DNET from which we match to the remaining-pattern.
  801.   ;; This function performs a search, where remaining-pattern directs 
  802.   ;; where to go.  The frontier expands when multiple matches to a variable
  803.   ;; in the ARGUMENT is found.  A new list of links (the final frontier)
  804.   ;; is returned.
  805.   (declare (list frontier) (optimize (safety 1) (space 2) (speed 3)))
  806.   (cond 
  807.  
  808.    ;; Remaining pattern is a variable: return list of continuations reached
  809.    ;; by consuming one item from each of the current continuations.
  810.    ((variable-p remaining-pattern)
  811.     (do ((lptr frontier (cdr lptr))
  812.          (new-frontier (list :head)))
  813.         ((null lptr) (cdr new-frontier))
  814.       (declare (list lptr new-frontier))
  815.       (nconc new-frontier 
  816.              (the list (continuations-after-pattern-variable (car lptr))))))
  817.  
  818.    ;; Remaining pattern is an atom: replace each link in the frontier 
  819.    ;; with the link reached after traversing the branch for that atom,
  820.    ;; or eliminate the link's branch from the search frontier if no match.
  821.    ((atom remaining-pattern)
  822.     (traverse-links remaining-pattern frontier))
  823.  
  824.    ;; Remaining pattern is a list.  Traverse a :begin-list token, and 
  825.    ;; iterate over the items in the pattern list, extending the search
  826.    ;; frontier along matching paths until the list is consumed.  Return
  827.    ;; the frontier reached by traversing :end-list links from the result.
  828.    ;; Exception: if the pattern ends in a dotted variable, consume what
  829.    ;; ever is required to finish the list in the DNET.
  830.    (T
  831.     (setf frontier (traverse-links :begin-list frontier))
  832.     (do ((pptr remaining-pattern (cdr pptr)))
  833.         ((atom pptr)
  834.          (if pptr
  835.            (if (variable-p pptr)
  836.              (mapcan #'continuations-after-consuming-list frontier)
  837.              (error "[DNET:MATCH-PATTERN] Nonvariable dotted ending is illegal:~%~S"
  838.                     remaining-pattern))
  839.            (traverse-links :end-list frontier)))
  840.       (declare (list pptr))
  841.       (setf frontier (pattern-match-links (car pptr) frontier))))))
  842. (proclaim '(function pattern-match-links (t list) list))
  843.  
  844. (defun EXPRESSION-MATCH-LINKS (remaining-expression frontier)
  845.   ;; Frontier is a list of links, the locations in the DNET from which
  846.   ;; we match to the remaining-expression.  This function performs a 
  847.   ;; search from frontier, with remaining-expression directing where to go.
  848.   ;; A node in the frontier expands when multiple matches to variables and 
  849.   ;; constants in the NET are found.  A list of links (the "final frontier")
  850.   ;; is returned.
  851.   (declare (list frontier) (optimize (safety 1) (space 2) (speed 3)))
  852.   (let ((new-frontier (list :head)))
  853.     (declare (list new-frontier))
  854.  
  855.     ;; Each link on the frontier represents the last token consumed (the car)
  856.     ;; and where we are now (the cdr).  If the cdr is an alist, it has links
  857.     ;; whose cars are to be matched to the remaining-expression.  If any of
  858.     ;; these cars is a variable, the entire remaining expression is consumed.
  859.     ;; Thus, we may return without further processing the places which matching
  860.     ;; to these variables get us -- namely, the cdrs of the variable links.
  861.     (dolist (flink frontier)
  862.       (declare (list flink))
  863.       (nconc new-frontier
  864.              (mapcan #'(lambda (link)
  865.                          (declare (list link))
  866.                          (if (variable-p (car link)) (list link) nil))
  867.                      (cdr flink))))
  868.  
  869.     ;; Now we have to do literal matching, to be combined with the variable
  870.     ;; continuations computed above.
  871.     (cond 
  872.  
  873.      ;; Atomic expression: For each frontier link, search the continuations
  874.      ;; available in its cdr for a continuing link with an EQUAL atom.  These
  875.      ;; are added to the links to be returned without further processing.
  876.      ((atom remaining-expression)
  877.       (dolist (flink frontier)
  878.         (declare (list flink))
  879.         (nconc new-frontier
  880.                (mapcan #'(lambda (link)
  881.                            (declare (list link))
  882.                            (if (equal (car link) remaining-expression) 
  883.                              (list link)
  884.                              nil))
  885.                        (cdr flink)))))
  886.  
  887.      ;; List expression: In addition to the variable-generated continuations
  888.      ;; computed above, we need to recurse to get the literal matches. Do this
  889.      ;; by expanding the frontier in parallel with iterating over the list.
  890.      (T
  891.       (setf frontier (traverse-links :begin-list frontier))
  892.       (do ((eptr remaining-expression (cdr eptr)))
  893.           ((null eptr) (nconc new-frontier (traverse-links :end-list frontier)))
  894.         (declare (list eptr))
  895.         (setf frontier (expression-match-links (car eptr) frontier)))))
  896.  
  897.     ;; Return all the continuations we have collected.
  898.     (cdr new-frontier)))
  899. (proclaim '(function expression-match-links (t list) list))
  900.  
  901. (defun MATCH-LINKS (remaining-pattern frontier)
  902.   ;; This is the combined version, which processes variables in both the
  903.   ;; remaining-pattern and the dnet.
  904.   (declare (list frontier) (optimize (safety 1) (space 2) (speed 3)))
  905.   (let ((new-frontier (list :head)))
  906.     (declare (list new-frontier))
  907.  
  908.     ;; Each link on the frontier represents the last token consumed (the car)
  909.     ;; and where we are now (the cdr).  If the cdr is an alist, it has links
  910.     ;; whose cars are to be matched to the remaining-pattern.  If any of
  911.     ;; these cars is a variable, the entire remaining expression is consumed.
  912.     ;; Thus, we may return without further processing the places which matching
  913.     ;; to these variables get us -- namely, the cdrs of the variable links.
  914.     (dolist (flink frontier)
  915.       (declare (list flink))
  916.       (nconc new-frontier
  917.              (mapcan #'(lambda (link)
  918.                          (declare (list link))
  919.                          (if (variable-p (car link)) (list link) nil))
  920.                      (cdr flink))))
  921.  
  922.     ;; Now we have to do matching to variables in the pattern, and to literals, 
  923.     ;; to be combined with the dnet variable continuations computed above.
  924.     (cond 
  925.  
  926.      ;; Remaining pattern is a variable: add list of continuations reached
  927.      ;; by consuming one item from each of the current continuations.
  928.      ((variable-p remaining-pattern)
  929.       (do ((lptr frontier (cdr lptr))
  930.            (continuations (list :head)))
  931.           ((null lptr) (nconc new-frontier (cdr continuations)))
  932.         (declare (list lptr continuations))
  933.         (nconc continuations
  934.                (the list (continuations-after-pattern-variable (car lptr))))))
  935.  
  936.      ;; Atomic expression: For each frontier link, search the continuations
  937.      ;; available in its cdr for a continuing link with an EQUAL atom.  These
  938.      ;; are added to the links to be returned without further processing.
  939.      ((atom remaining-pattern)
  940.       (dolist (flink frontier)
  941.         (declare (list flink))
  942.         (nconc new-frontier
  943.                (mapcan #'(lambda (link)
  944.                            (declare (list link))
  945.                            (if (equal (car link) remaining-pattern) 
  946.                              (list link)
  947.                              nil))
  948.                        (cdr flink)))))
  949.  
  950.      ;; List expression: In addition to the variable-generated continuations
  951.      ;; computed above, we need to recurse to get the literal matches. Do this
  952.      ;; by expanding the frontier in parallel with iterating over the list.
  953.      (T
  954.       (setf frontier (traverse-links :begin-list frontier))
  955.       (do ((eptr remaining-pattern (cdr eptr)))
  956.           ((null eptr) 
  957.            (nconc new-frontier (the list (traverse-links :end-list frontier))))
  958.         (declare (list eptr))
  959.         (setf frontier (match-links (car eptr) frontier)))))
  960.  
  961.     ;; Return all the continuations we have collected.  Duplicates arise when
  962.     ;; variables match to variables.
  963.     (delete-duplicates (cdr new-frontier))))
  964. (proclaim '(function match-links (t list) list))
  965.  
  966. ;;; --------------------------------------------------------------------------
  967. ;;; NOTE the next three functions are "internal" but used by the RULE package.
  968. ;;; In particular, previous-bindings is used for consistency filtering.
  969.  
  970. (defun MATCH-PATTERN-INTERNAL (pattern dnet previous-bindings)
  971.   (declare (symbol dnet) (inline pattern-match-links) (list previous-bindings)
  972.            (optimize (safety 1) (space 2) (speed 3)))
  973.   (let ((matching nil) (bindings nil))
  974.     (declare (list matching bindings))
  975.     (dolist (link (the list 
  976.                        (pattern-match-links
  977.                         pattern
  978.                         (list (dnet-link 
  979.                                (the dnet (sm:gets 'dnet dnet)))))))
  980.       (declare (list link))
  981.       (multiple-value-bind 
  982.         (success-p binding)
  983.         (bind-vars pattern (dnet-terminal-expr (cdr link)) previous-bindings)
  984.         (declare (symbol success-p) (list binding))
  985.         (when success-p
  986.           (push (dnet-terminal-expr (cdr link)) matching)
  987.           (push binding bindings))))
  988.     (values matching bindings)))
  989. (proclaim '(function match-pattern-internal (t symbol list) (values list list)))
  990.  
  991. (defun MATCH-EXPRESSION-INTERNAL (expression dnet previous-bindings)
  992.   (declare (inline expression-match-links) (symbol dnet) (list previous-bindings)
  993.            (optimize (safety 1) (space 2) (speed 3)))
  994.   (let ((matching nil) (bindings nil))
  995.     (declare (list matching bindings))
  996.     (dolist (link (delete-duplicates ; in case expression has variables.
  997.                    (the list 
  998.                         (expression-match-links
  999.                          expression
  1000.                          (list (dnet-link 
  1001.                                 (the dnet (sm:gets 'dnet dnet))))))))
  1002.       (declare (list link))
  1003.       (multiple-value-bind 
  1004.         (success-p binding)
  1005.         (bind-vars (dnet-terminal-expr (cdr link)) expression previous-bindings)
  1006.         (declare (symbol success-p) (list binding))
  1007.         (when success-p
  1008.           (push (dnet-terminal-expr (cdr link)) matching)
  1009.           (push binding bindings))))
  1010.     (values matching bindings)))
  1011. (proclaim '(function match-expression-internal (t symbol list) (values list list)))
  1012.  
  1013. (defun MATCH-INTERNAL (pattern dnet)
  1014.   (declare (symbol dnet) (inline match-links) 
  1015.            (optimize (safety 1) (space 2) (speed 3)))
  1016.   (let ((matching nil) (bindings-1 nil) (bindings-2 nil))
  1017.     (declare (list matching bindings-1 bindings-2))
  1018.     (dolist (link (the list
  1019.                        (match-links pattern
  1020.                                     (list (dnet-link
  1021.                                            (the dnet (sm:gets 'dnet dnet)))))))
  1022.       (declare (list link))
  1023.       (multiple-value-bind
  1024.         (success-p binding-1 binding-2)
  1025.         (unify pattern (dnet-terminal-expr (cdr link)) nil nil)
  1026.         (declare (symbol success-p) (list binding-1 binding-2))
  1027.         (when success-p
  1028.           (push (dnet-terminal-expr (cdr link)) matching)
  1029.           (push binding-1 bindings-1)
  1030.           (push binding-2 bindings-2))))
  1031.     (values matching bindings-1 bindings-2)))
  1032. (proclaim '(function match-internal (t symbol) (values list list list)))
  1033.  
  1034. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1035. ;;;
  1036. ;;;                    EXPORTED OPERATIONS ON DNETs
  1037. ;;;
  1038. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1039.  
  1040. (defun MAKE-DNET (name &key indexpr-hook delexpr-hook info)
  1041.   "make-dnet <name> &key <indexpr-hook> <delexpr-hook> <info>       [Function]
  1042.    Returns a new (empty) discrimination net.  A name is generated unless
  1043.    a symbol <name> is provided.  <Indexpr-hook> and <delexpr-hook> are
  1044.    assumed to be lambda forms of two arguments which may be compiled.
  1045.    These functions are applied to an expression and its corresponding 
  1046.    DNET-TERMINAL the first time it is indexed into or deleted from the 
  1047.    DNET, respectively. If the optional <info> is provided, the DNET's 
  1048.    associated information is initialized to this value."
  1049.   (check-type name symbol)
  1050.   (check-type indexpr-hook list)
  1051.   (check-type delexpr-hook list)
  1052.   (let ((dnet-name (or name (gentemp "DNET-"))))
  1053.     (declare (symbol dnet-name))
  1054.     (create-dnet dnet-name indexpr-hook delexpr-hook info)
  1055.     (setf (dnet-link (the dnet (sm:gets 'dnet dnet-name)))
  1056.           (list dnet-name))
  1057.     (when indexpr-hook
  1058.       (setf (dnet-compiled-indexpr-hook
  1059.              (the dnet (sm:gets 'dnet dnet-name)))
  1060.           (compile nil indexpr-hook)))
  1061.     (when delexpr-hook
  1062.       (setf (dnet-compiled-delexpr-hook
  1063.              (the dnet (sm:gets 'dnet dnet-name)))
  1064.           (compile nil delexpr-hook)))
  1065.     dnet-name))
  1066. (proclaim '(function make-dnet (symbol &key list list t) symbol))
  1067.  
  1068. (defmacro DNET-INFO (dnet)
  1069.   "dnet-info <dnet>                                                    [Macro]
  1070.   Setf-able access to the information associated with <dnet>."
  1071.   `(dnet-info-place (the dnet (sm:gets 'dnet ,dnet))))
  1072.  
  1073. (defun RESET-DNET (dnet &key (indexpr-hook nil)
  1074.                              (delexpr-hook nil)
  1075.                              (info nil info-supplied))
  1076.   "reset-dnet <dnet> &key <indexpr-hook> <delexpr-hook> <info>      [Function]
  1077.   Empties an existing discrimination net.  Also enables one to modify
  1078.   the hooks and associated info.  See MAKE-DNET."
  1079.   (check-type dnet symbol)
  1080.   (check-type indexpr-hook list)
  1081.   (check-type delexpr-hook list)
  1082.   (assert (sm:gets 'dnet dnet) (dnet)
  1083.           "[DNET:RESET-DNET] ~S is not a known DNET." dnet)
  1084.   (setf (dnet-link (the dnet (sm:gets 'dnet dnet))) (list dnet))
  1085.   (when indexpr-hook
  1086.     (setf (dnet-compiled-indexpr-hook (the dnet (sm:gets 'dnet dnet)))
  1087.           (compile nil indexpr-hook)))
  1088.   (when delexpr-hook
  1089.     (setf (dnet-compiled-delexpr-hook (the dnet (sm:gets 'dnet dnet)))
  1090.           (compile nil delexpr-hook)))
  1091.   (when info-supplied (setf (dnet-info dnet) info))
  1092.   dnet)
  1093.  
  1094. (defun DESTROY-DNET (dnet)
  1095.   "destroy-dnet <dnet>                                              [Function]
  1096.   Destroys and undefines the entire <dnet>."
  1097.   (check-type dnet symbol)
  1098.   (assert (sm:gets 'dnet dnet) (dnet)
  1099.           "[DNET:DESTROY-DNET] Unknown DNET ~S" dnet)
  1100.   (sm:destroys 'dnet dnet))
  1101. (proclaim '(function destroy-dnet (symbol) symbol))
  1102.  
  1103. (defvariable expr) ; used below
  1104.  
  1105. (defun ALL-EXPRESSIONS (dnet)
  1106.   "all-expressions <dnet>                                           [Function]
  1107.   Returns a list of all expressions in the indicated <dnet>.  The outer
  1108.   list is constructed fresh and may be hacked.  This is time consuming."
  1109.   (declare (inline pattern-match-links) (optimize (safety 1) (space 2) (speed 3)))
  1110.   (check-type dnet symbol)
  1111.   (assert (sm:gets 'dnet dnet) (dnet)
  1112.           "[DNET:ALL-EXPRESSIONS] Unknown DNET ~S" dnet)
  1113.   ;; I don't use match-pattern-internal since that conses up un-needed bindings.
  1114.   (mapcar #'(lambda (link)
  1115.               (declare (list link))
  1116.               (dnet-terminal-expr (cdr link)))
  1117.           (the list 
  1118.                 (pattern-match-links
  1119.                    '?:expr
  1120.                    (list (dnet-link 
  1121.                             (the dnet (sm:gets 'dnet dnet))))))))
  1122. (proclaim '(function all-expressions (symbol) list))
  1123.  
  1124. (defun MAP-DNET-TERMINALS (f dnet)
  1125.   "map-dnet-terminals <f> <dnet>                                     [Function]
  1126.   Maps <f> across dnet-terminals in the indicated <dnet>.  Returns NIL."
  1127.   (declare (inline pattern-match-links) (optimize (safety 1) (space 2) (speed 3)))
  1128.   (check-type dnet symbol)
  1129.   (assert (sm:gets 'dnet dnet) (dnet)
  1130.           "[DNET:MAP-DNET-TERMINALS] Unknown DNET ~S" dnet)
  1131.   ;; I don't use match-pattern-internal since that conses up un-needed bindings.
  1132.   ;; It is important to ensure that an <f> that calls DELEXPR won't crash this.
  1133.   (map nil #'(lambda (link) (declare (list link)) (funcall f (cdr link)))
  1134.        (the list 
  1135.             (pattern-match-links
  1136.              '?:expr
  1137.              (list (dnet-link 
  1138.                     (the dnet (sm:gets 'dnet dnet))))))))
  1139. (proclaim '(function map-dnet-terminals (function symbol) null))
  1140.  
  1141. (defun SAVE-DNET (dnet path &optional 
  1142.                        (write-in-package *dnet-package*) &aux (vars ()))
  1143.   "save-dnet <dnet> <path> &optional (write-in-package :dnet)       [Function]
  1144.    Saves expressions required to recreate <dnet> to a file at <path>.
  1145.    Returns the <path>.  The file is written in package <write-in-package>,
  1146.    or DNET if the optional argument is unspecified. All associated info
  1147.    is also saved.   Give <vars> a list of known variables.  This function 
  1148.    is slow."
  1149.   (declare (list vars))
  1150.   (check-type dnet symbol)
  1151.   (assert (sm:gets 'dnet dnet) (dnet)
  1152.           "[DNET:SAVE-DNET] Unknown DNET ~S" dnet)
  1153.   (check-type path (or simple-string pathname))
  1154.   (when (not (typep write-in-package 'package))
  1155.     (setf write-in-package (find-package (string write-in-package))))
  1156.   (assert write-in-package (write-in-package) 
  1157.           "[DNET:SAVE-DNET] Bad package specified.")
  1158.   (let ((*package* write-in-package) (*print-pretty* t) (*print-escape* t)
  1159.         (*print-circle* nil) (*print-case* :upcase) (*print-array* t)
  1160.         #+:ccl (ccl::*print-structure* t)
  1161.         (dnet-struct (sm:gets 'dnet dnet))
  1162.         (dnet-terminals
  1163.           (mapcar #'cdr 
  1164.                    (pattern-match-links
  1165.                       '?:expr
  1166.                       (list (dnet-link 
  1167.                                 (the dnet (sm:gets 'dnet dnet))))))))
  1168.     (declare (type dnet dnet-struct) (list dnet-terminals)
  1169.              (optimize (safety 1) (space 2) (speed 3)))
  1170.     (with-open-file (stream path
  1171.                             :direction :output
  1172.                             :if-exists :supersede)
  1173.       (format stream ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1174. ;;; Discrimination Net ~S~%;;; Saved by SAVE-DNET ~A~%;;; On ~A, a ~A"
  1175.               dnet
  1176.               (multiple-value-bind
  1177.                   (second minute hour date month year)
  1178.                   (get-decoded-time)
  1179.                   (declare (integer second minute hour date month year))
  1180.                   (format nil "~2,'0D-~A-~2,'0D ~2,'0D:~2,'0D:~2,'0D"
  1181.                             date 
  1182.                             (case month
  1183.                                   ((1) "Jan") ((2) "Feb") ((3) "Mar") ((4) "Apr")
  1184.                                   ((5) "May") ((6) "Jun") ((7) "Jul") ((8) "Aug")
  1185.                                   ((9) "Sep") ((10) "Oct") ((11) "Nov") ((12) "Dec"))
  1186.                             (- year 1900)
  1187.                             hour minute second))
  1188.               (machine-instance)
  1189.               (machine-type))
  1190.       (format stream "~%~%(in-package ~S)~%" (package-name write-in-package))
  1191.       (dolist (term dnet-terminals)
  1192.         (declare (simple-vector term)) ; since dnet-terminal is this :type
  1193.         ;; Variables may be in INFO as well as EXPR.  
  1194.         (setf vars 
  1195.               (nunion (nunion (variables-in-pattern (dnet-terminal-expr term)) 
  1196.                               (variables-in-pattern (dnet-terminal-info term)))
  1197.                       vars)))
  1198.       (dolist (v (sort vars #'(lambda (s1 s2) 
  1199.                                 (string< (symbol-name s1) (symbol-name s2)))))
  1200.         (format stream "~%(dnet:defvariable ?::~A)" v))
  1201.       (format stream "~%~%(dnet:make-dnet '~S~
  1202.                       ~%  :indexpr-hook '~A~
  1203.                       ~%  :delexpr-hook '~A~
  1204.                       ~%  :info '~S)~%"
  1205.               dnet
  1206.               (prin1-to-string (dnet-indexpr-hook dnet-struct))
  1207.               (prin1-to-string (dnet-delexpr-hook dnet-struct))
  1208.               (dnet-info-place   dnet-struct))
  1209.       (dolist (term dnet-terminals)
  1210.         (declare (simple-vector term)) ; since dnet-terminal is this :type
  1211.         (format stream "~%(dnet::indexpr-internal '~S '~S '~S)"
  1212.                  (dnet-terminal-expr term) dnet (dnet-terminal-info term)))
  1213.       (format stream "~&~%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1214. ;;; EOF"))
  1215.     path))
  1216. (proclaim '(function save-dnet 
  1217.            (symbol (or simple-string pathname) 
  1218.                    &optional (or null string package))
  1219.            pathname))
  1220.  
  1221. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1222. ;;;
  1223. ;;;                   EXPORTED OPERATIONS ON EXPRESSIONS
  1224. ;;;
  1225. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1226.  
  1227. (defun INDEXPR (expr dnet &optional info)
  1228.   "indexpr <expr> <dnet> &optional <info>                           [Function]
  1229.   Ensures that <expr> is stored in <dnet>.  If the <expr> was not already
  1230.   in <dnet>, initializes the associated information to <info> (if it was 
  1231.   provided), and calls compiled-new-expr-hook (if non-nil) on <expr> and 
  1232.   its dnet-terminal.  The latter allows application-specific processing of 
  1233.   new expressions. Returns two values: the first is a predicate, T iff <expr> 
  1234.   was newly added by this call; and the second is the dnet-terminal structure."
  1235.   (declare (inline indexpr-internal))
  1236.   (assert (not-a-dotted-list expr) (expr)
  1237.           "[DNET:INDEXPR] Dotted lists not allowed in DNET: ~S" expr)
  1238.   (check-type dnet symbol)
  1239.   (assert (sm:gets 'dnet dnet) (dnet)
  1240.           "[DNET:INDEXPR] Unknown DNET ~S" dnet)
  1241.   (indexpr-internal expr dnet info))
  1242. (proclaim '(function indexpr (t symbol &optional t) (values boolean dnet-terminal)))
  1243.  
  1244. (defun GETEXPR (expr dnet)
  1245.   "getexpr <expr> <dnet>                                            [Function]
  1246.   Use to query whether <expr> has been stored in <dnet>, and to obtain the
  1247.   name of its dnet-terminal.  Returns two values: the expression originally
  1248.   stored in <dnet> (and is EQUAL to <expr>), and the dnet-terminal structure.
  1249.   Both values are Nil if <expr> is not found. Variables are not processed."
  1250.   (declare (inline getexpr-internal))
  1251.   (check-type dnet symbol)
  1252.   (assert (sm:gets 'dnet dnet) (dnet) "[DNET:GETEXPR] Unknown DNET ~S" dnet)
  1253.   (getexpr-internal expr dnet))
  1254. (proclaim '(function getexpr (t symbol) (values t dnet-terminal)))
  1255.  
  1256. (defun DELEXPR (expr dnet) 
  1257.   "delexpr <expr> <dnet>                                            [Function]
  1258.   Deletes the expression <expr> from <dnet>, calling DELEXPR-HOOK if it
  1259.   is defined for the DNET.  Returns a DNET-TERMINAL iff it was deleted."
  1260.   (declare (inline delexpr-internal))
  1261.   (check-type dnet symbol)
  1262.   (assert (sm:gets 'dnet dnet) (dnet)
  1263.           "[DNET:DELEXPR] Unknown DNET ~S" dnet)
  1264.   (delexpr-internal expr dnet))
  1265. (proclaim '(function delexpr (t symbol) (or null dnet-terminal)))
  1266.  
  1267. ;;; Had to be a function for safety (multiple evaluation, ...) but need setf.
  1268.  
  1269. (defun EXPR-INFO (expr dnet)
  1270.   "expr-info <expr> <dnet>                                          [Function]
  1271.   Setf-able access to the information associated with <expr> in <dnet>."
  1272.   (check-type dnet symbol)
  1273.   (assert (sm:gets 'dnet dnet) (dnet)
  1274.           "[DNET:EXPR-INFO] Unknown DNET ~S" dnet)
  1275.   (expr-info-internal expr dnet))
  1276. (proclaim '(function expr-info (t symbol) t))
  1277.  
  1278. ;;; Internal users will setf the internal macro directly, but externals need this.
  1279. (defun set-expr-info (expr dnet value)
  1280.   (check-type dnet symbol)
  1281.   (assert (sm:gets 'dnet dnet) (dnet)
  1282.           "[DNET:EXPR-INFO] Unknown DNET ~S" dnet)
  1283.   (setf (expr-info-internal expr dnet) value))
  1284. (defsetf expr-info set-expr-info)
  1285. (proclaim '(function set-expr-info (t symbol t) t))
  1286.  
  1287. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1288. ;;;
  1289. ;;;                    EXPORTED OPERATIONS ON PATTERNS
  1290. ;;;
  1291. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1292.  
  1293. (defun MATCH-PATTERN (pattern dnet)
  1294.   "match-pattern <pattern> <dnet>                                   [Function]
  1295.   For retrieving all expressions matching a pattern which may contain
  1296.   variables.  Returns two values: a list of all expressions in <dnet>
  1297.   matching the <pattern>, and a list of respective unifications.  The
  1298.   latter is a list of lists containing pairs (<pat-var> . <dnet-exp>)
  1299.   representing the binding of <pat-var> to <dnet-exp>, a component of
  1300.   the corresponding returned expression.  Variables in the dnet are 
  1301.   treated as constants.  A special facility provided only by this
  1302.   function is dotted variables: any sublist of <pattern> may end in 
  1303.   a dot followed by a variable.  This is like &rest binding."
  1304.   (declare (inline match-pattern-internal))
  1305.   (check-type dnet symbol)
  1306.   (assert (sm:gets 'dnet dnet) (dnet)
  1307.           "[DNET:MATCH-PATTERN] Unknown DNET ~S" dnet)
  1308.   (match-pattern-internal pattern dnet nil))
  1309. (proclaim '(function match-pattern (t symbol) (values list list)))
  1310.  
  1311. (defun MATCH-EXPRESSION (expression dnet) 
  1312.   "match-expression <expression> <dnet>                             [Function]
  1313.   For retrieving all patterns (which may contain variables) matching an
  1314.   expression.  Returns two values: a list of all patterns in <dnet> 
  1315.   matching the <expression>, and a list of respective unifications.  The
  1316.   latter is a list of lists containing pairs (<exp-part> . <pat-var>) 
  1317.   representing the binding of <pat-var> to <exp-part>, a component of 
  1318.   <expression>. Variables in <expression> are treated as constants."
  1319.   (declare (inline match-expression-internal))
  1320.   (check-type dnet symbol)
  1321.   (assert (sm:gets 'dnet dnet) (dnet)
  1322.           "[DNET:MATCH-EXPRESSION] Unknown DNET ~S" dnet)
  1323.   (match-expression-internal expression dnet nil))
  1324. (proclaim '(function match-expression (t symbol) (values list list)))
  1325.  
  1326. (defun MATCH (pattern dnet)
  1327.   "match <pattern> <dnet>                                           [Function]
  1328.   For retrieving all patterns unifying with a pattern: variables in both
  1329.   are processed.  Returns three values: a list of all patterns in <dnet>
  1330.   unifying with the <pattern>, a list of bindings of variables in <pattern>
  1331.   to those in the matched patterns, and a list of bindings of variables in
  1332.   the matched patterns to those in <pattern>.  The latter two values are
  1333.   lists of lists containing pairs (<var> . <exp>), each representing the
  1334.   binding of <var> to <exp> (ditto).  Note this is less efficient than
  1335.   MATCH-PATTERN and MATCH-EXPRESSION, and should only be used when needed.
  1336.   See also documentation for UNIFY."
  1337.   (declare (inline match-internal))
  1338.   (check-type dnet symbol)
  1339.   (assert (sm:gets 'dnet dnet) (dnet)
  1340.           "[DNET:MATCH] Unknown DNET ~S" dnet)
  1341.   (match-internal pattern dnet))
  1342. (proclaim '(function match (t symbol) (values list list list)))
  1343.  
  1344. (defun BIND-VARS (pattern expression bindings)
  1345.   "bind-vars <pattern> <expression> <bindings>                      [Function]
  1346.   Variables are processed in <pattern> but treated as atoms in <expression>.
  1347.   <Bindings> should be existing bindings (usually nil). Dotted endings in 
  1348.   <pattern> are assumed to be variables, and are processed. Returns two 
  1349.   values: T or NIL to flag whether the pattern matches the expression, and 
  1350.   a list of bindings which achieve this matching."
  1351.   (declare (optimize (safety 1) (space 2) (speed 3)))
  1352.   (cond
  1353.    ;; Variable: See if a previous binding for the variable exists.  If so,
  1354.    ;; the expression must be equal.  Otherwise add the new binding.
  1355.    ((variable-p pattern)
  1356.     (let ((binding (assoc pattern bindings)))
  1357.       (declare (list binding))
  1358.       (if binding
  1359.         (if (equal (cdr binding) expression)
  1360.           (values T bindings)
  1361.           (values nil nil))
  1362.         (values T (push (cons pattern expression) bindings)))))
  1363.  
  1364.    ;; If the pattern is a non-variable atom, then it must be equal to
  1365.    ;; the expression, because the matcher tests for this.
  1366.    ((atom pattern) (values T bindings))
  1367.  
  1368.    ;; Otherwise both are lists: iterate over items in lists in parallel
  1369.    ;; (avoiding double recursion, empirically faster), seeing if the
  1370.    ;; corresponding items bind, and adding any necessary bindings.
  1371.    (T ; Allegro Common Lisp won't return two values if DO put first.
  1372.     (do ((pattern-ptr pattern (cdr pattern-ptr)) 
  1373.          (expression-ptr expression (cdr expression-ptr)))
  1374.         ;; Matcher guarantees the lists are the same length; don't have
  1375.         ;; to test (null expression-ptr).
  1376.         ((null pattern-ptr) (values T bindings))
  1377.       (declare (list pattern-ptr expression-ptr))
  1378.       (if (atom pattern-ptr) ; won't be nil
  1379.         (let ((binding (assoc pattern-ptr bindings)))
  1380.           (declare (list binding))
  1381.           (if binding
  1382.             (if (not (equal (cdr binding) expression-ptr))
  1383.               (return (values nil nil))  ; blow out of loop
  1384.               (setf pattern-ptr    nil
  1385.                     expression-ptr nil)) ; exit normally next pass
  1386.             (progn
  1387.               (push (cons pattern-ptr expression-ptr) bindings)
  1388.               (setf pattern-ptr nil))))
  1389.         (multiple-value-bind
  1390.           (success-p new-bindings)
  1391.           (bind-vars (car pattern-ptr) (car expression-ptr) bindings)
  1392.           (declare (symbol success-p) (list new-bindings))
  1393.           (if success-p
  1394.             (setf bindings new-bindings)
  1395.             (return (values nil nil)))))))))
  1396. (proclaim '(function bind-vars (t t list) (values boolean list)))
  1397.  
  1398. (defun SUBSTITUTE-BINDINGS (bindings pattern)
  1399.   "substitute-bindings <bindings> <pattern>                         [Function]
  1400.   Given <bindings> is an association list as returned by one of the match
  1401.   functions, creates an expression from <pattern> where all the variables
  1402.   have been replaced by their bindings.  New list structure is used.
  1403.   Only makes one pass -- see substitute-transitive-bindings if variables
  1404.   may be bound to each other."
  1405.   (check-type bindings list)
  1406.   (labels ((substitute-bindings-r (bindings pattern &aux binding)
  1407.          (declare (list bindings binding) 
  1408.                       (optimize (safety 1) (space 2) (speed 3)))
  1409.          (cond ((null pattern) nil)
  1410.            ((atom pattern)
  1411.             (if (and (variable-p pattern)
  1412.                  (setf binding (assoc pattern bindings)))
  1413.               (cdr binding)
  1414.               pattern))
  1415.            (t
  1416.             (cons (substitute-bindings-r bindings (car pattern))
  1417.               (substitute-bindings-r bindings (cdr pattern)))))))
  1418.     (substitute-bindings-r bindings pattern)))
  1419. (proclaim '(function substitute-bindings (list t) t))
  1420.  
  1421. (defun SUBSTITUTE-TRANSITIVE-BINDINGS (bindings pattern)
  1422.   "substitute-bindings <bindings> <pattern>                         [Function]
  1423.   Given <bindings> is an association list as returned by one of the match
  1424.   functions, creates an expression from <pattern> where all the variables
  1425.   have been replaced by their bindings.  New list structure is used.  
  1426.   Makes as many passes are needed to eliminate transitivities which may
  1427.   be returned by UNIFY when both patterns have variables, such as 
  1428.   ((?:y . 3) (?:x . ?:y)) which unifies (?:x ?:x) and (?:y 3)."
  1429.   (check-type bindings list)
  1430.   (let ((changed nil))
  1431.     (labels ((substitute-bindings-r (bindings pattern &aux binding)
  1432.                 (declare (list bindings binding)
  1433.                          (optimize (safety 1) (space 2) (speed 3)))
  1434.                 (cond ((null pattern) nil)
  1435.                       ((atom pattern)
  1436.                        (if (and (variable-p pattern)
  1437.                                 (setf binding (assoc pattern bindings)))
  1438.                          (progn (setq changed t) (cdr binding))
  1439.                          pattern))
  1440.                       (t
  1441.                        (cons (substitute-bindings-r bindings (car pattern))
  1442.                              (substitute-bindings-r bindings (cdr pattern)))))))
  1443.       (let ((new-pattern (substitute-bindings-r bindings pattern)))
  1444.         (if changed 
  1445.           (substitute-transitive-bindings bindings new-pattern)
  1446.           new-pattern)))))
  1447. (proclaim '(function substitute-transitive-bindings (list t) t))
  1448.  
  1449. (defun UNIFY (pattern-1 pattern-2 &optional (bindings-1 nil) (bindings-2 nil))
  1450.   "unify <pattern-1> <pattern-2> &optional <bindings-1> <bindings-2> [Function]
  1451.   Given two list/atom patterns (each of which may contain variables), and
  1452.   a (usually nil) set of initial bindings, returns three values: 
  1453.     1. T or NIL to flag whether the patterns unify;
  1454.     2. Bindings of variables in <pattern-1> to elements of <pattern-2>;
  1455.     3. Bindings of variables in <pattern-2> to elements of <pattern-1>.
  1456.   (The flag is returned first so UNIFY can be used as a predicate.  Since 
  1457.   unification can succeed with no bindings, the other values will not serve 
  1458.   this function.)  Example:
  1459.     (unify '(a ?:x c) '(a (b) ?:y)) ==> T; ((?:X B)); ((?:Y . C))
  1460.   Transitivity of bindings is not used for simplification, e.g. you could
  1461.   get back T; ((?:X . ?:Y)); ((?:Y . C)). Modified from version by Ken Forbus.
  1462.   This function is logically general, and hence checks for a variety of 
  1463.   conditions.  It may be inefficient for specialized tasks: I recommend 
  1464.   writing a specialized version if any assumptions may be made about the
  1465.   patterns to be unified."
  1466.   (declare (list bindings-1 bindings-2) (optimize (safety 1) (space 2) (speed 3)))
  1467.  
  1468.   (cond ((equal pattern-1 pattern-2) (values t bindings-1 bindings-2))
  1469.         ((variable-p pattern-1) 
  1470.          (unify-variable-1 pattern-1 pattern-2 bindings-1 bindings-2))
  1471.         ((variable-p pattern-2) 
  1472.          (unify-variable-2 pattern-1 pattern-2 bindings-1 bindings-2))
  1473.         ((or (not (listp pattern-1)) (not (listp pattern-2))) 
  1474.          (values nil nil nil))
  1475.         (t
  1476.          (multiple-value-bind
  1477.            (success new-bindings-1 new-bindings-2)
  1478.            (unify (first pattern-1) (first pattern-2) bindings-1 bindings-2)
  1479.            (if success 
  1480.              (unify (rest pattern-1) (rest pattern-2) new-bindings-1 new-bindings-2)
  1481.              (values nil nil nil))))))
  1482. (proclaim '(function unify (T T &optional list list) (values boolean list list)))
  1483.  
  1484. ;;; These three were lexically scoped in UNIFY, but the VAX compile warned of
  1485. ;;; "not declared or defined" (I suppose you have to declare lexically scoped
  1486. ;;; functions as such?).  Besides, tests show lexical scoping is slower!!!
  1487.  
  1488. (defun FREE-IN? (variable pattern bindings)
  1489.   ;; Determines whether a variable is free in a pattern.
  1490.   (cond ((null pattern) t)
  1491.         ((eq variable pattern) nil)
  1492.         ((variable-p pattern)
  1493.          (free-in? variable (utils:image pattern bindings) bindings))
  1494.         ((not (listp pattern)) t)
  1495.         ((free-in? variable (first pattern) bindings)
  1496.          (free-in? variable (rest pattern) bindings))))
  1497.  
  1498. (defun UNIFY-VARIABLE-1 (variable pattern bindings-1 bindings-2 &aux value)
  1499.   ;; Deals with case where pattern-1 is a variable.
  1500.   (cond ((setq value (utils:image variable bindings-1))
  1501.          (unify value pattern bindings-1 bindings-2))
  1502.         ((free-in? variable pattern bindings-2)
  1503.          (values t (cons (cons variable pattern) bindings-1) bindings-2))
  1504.         (t (values nil nil nil))))
  1505.  
  1506. (defun UNIFY-VARIABLE-2 (pattern variable bindings-1 bindings-2 &aux value)
  1507.   ;; Deals with case where pattern-2 is a variable.
  1508.   (cond ((setq value (utils:image variable bindings-2))
  1509.          (unify pattern value bindings-1 bindings-2))
  1510.         ((free-in? variable pattern bindings-1)
  1511.          (values t bindings-1 (cons (cons variable pattern) bindings-2)))
  1512.         (t (values nil nil nil))))
  1513.  
  1514. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1515. (provide :DNET)
  1516. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1517. ;;; EOF
  1518.    
  1519.